package com.zmc.third.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.zmc.common.utils.uuid.IdUtils;
import com.zmc.third.exception.LogisticsException;
import com.zmc.third.infrastructure.bean.logistics.*;
import com.zmc.third.infrastructure.bean.logistics.sf.SFRouteQuery;
import com.zmc.third.infrastructure.bean.logistics.sf.SFRouteResponse;
import com.zmc.third.infrastructure.bean.resp.FreightThirdCreateOrderResp;
import com.zmc.third.infrastructure.bean.resp.FreightQueryOrderResp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import sun.misc.BASE64Encoder;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @Author：zmc
 * @Date：2025/1/7 17:36
 */

@Component
public class SFService {

    public static final Logger log = LoggerFactory.getLogger(SFService.class);

    @Autowired
    private RestTemplate restTemplate;

    //测试地址
    private static final String TEST_URL = "https://sfapi-sbox.sf-express.com/std/service";
    private static final String TEST_PARTNER_ID = "YM0AR2LJ";
    private static final String TEST_MD5_KEY = "rfPVsR4IVfMfCOexJ7JN4zuAfJdy1sTn";
    //正式地址 final
    private static final String SF_URL = "https://sfapi.sf-express.com/std/service";
    private static final String SF_PARTNER_ID = "";
    private static final String SF_MD5_KEY = "";


    /**
     * 寄件下单
     */
    public FreightThirdCreateOrderResp createOrder(RequestCreate requestCreate) {
        FreightThirdCreateOrderResp resp = new FreightThirdCreateOrderResp();
        resp.setSuccess(true);
        resp.setRepeatOrder(false);
        String mgsData = JSON.toJSONString(requestCreate);
        String result = http("EXP_RECE_CREATE_ORDER", mgsData);
        resp.setResult(result);
        SFReturnData sfReturnData = JSON.parseObject(result, SFReturnData.class);
        log.info("寄件下单,result:{}", JSON.toJSONString(sfReturnData));
        if (!"A1000".equals(sfReturnData.getApiResultCode())) {
            throw new LogisticsException(sfReturnData.getApiErrorMsg());
        }
        String apiResultData = sfReturnData.getApiResultData();
        ResponseCreate responseCreate = JSON.parseObject(apiResultData, ResponseCreate.class);
        if (responseCreate.isSuccess()) {
            String code = JSON.parseObject(result).getJSONObject("apiResultData").getJSONObject("msgData").getJSONArray("waybillNoInfoList").getJSONObject(0).getString("waybillNo");
            resp.setWmsOrderId(code);
        } else {
            log.error(sfReturnData.toString());
            JSONObject jsonObject = JSON.parseObject(apiResultData);
            //重复下单
            if ("8016".equals(jsonObject.getString("errorCode"))) {
                resp.setSuccess(false);
                resp.setRepeatOrder(true);
            }
        }
        return resp;
    }


    /**
     * 价格查询
     */
    public BigDecimal queryPrice(PriceQuery priceQuery) {
        String mgsData = JSON.toJSONString(priceQuery);
        String result = http("EXP_RECE_QUERY_DELIVERTM", mgsData);

        JSONObject jsonObject = JSONObject.parseObject(result);
        String code = jsonObject.getString("apiResultCode");
        if (!"A1000".equals(code)) {
            log.error("价格查询,result:{}", result);
            throw new LogisticsException("顺丰接口->价格查询异常");
        }
        JSONObject apiResultData = jsonObject.getJSONObject("apiResultData");
        if (!apiResultData.getBoolean("success")) {
            log.error("价格查询,result:{}", result);
            throw new LogisticsException("顺丰接口->价格查询失败");
        }
        return apiResultData.getJSONObject("msgData").getJSONArray("deliverTmDto").getJSONObject(0).getBigDecimal("fee");
    }


    /**
     * 取消订单
     */
    public boolean cancel(OrderConformCancelRequest request) {
        String mgsData = JSON.toJSONString(request);
        String result = http("EXP_RECE_UPDATE_ORDER", mgsData);
        JSONObject jsonObject = JSONObject.parseObject(result);
        String code = jsonObject.getString("apiResultCode");
        if (!"A1000".equals(code)) {
            log.error("顺丰->取消订单异常，result:{}", result);
            throw new LogisticsException("顺丰取消订单失败");
        }
        JSONObject apiResultData = jsonObject.getJSONObject("apiResultData");
        return apiResultData.getBoolean("success");
    }

    /**
     * 订单下单结果查询(是否下单成功)
     */
    public FreightQueryOrderResp queryResult(OrderConformCancelRequest query) {
        FreightQueryOrderResp resp = new FreightQueryOrderResp();
        resp.setSuccess(true);
        String mgsData = JSON.toJSONString(query);
        String result = http("EXP_RECE_SEARCH_ORDER_RESP", mgsData);
        resp.setResult(result);
        JSONObject jsonObject = JSONObject.parseObject(result);
        String code = jsonObject.getString("apiResultCode");
        if (!"A1000".equals(code)) {
            log.error("顺丰下单结果查询异常,result: {}", result);
            throw new LogisticsException("顺丰下单结果查询异常");
        }
        JSONObject apiResultData = jsonObject.getJSONObject("apiResultData");
        Boolean success = apiResultData.getBoolean("success");
        if (!success) {
            resp.setSuccess(false);
        } else {
            resp.setWmsOrderSn(apiResultData.getJSONObject("msgData").getJSONArray("routeLabelInfo").getJSONObject(0).getJSONObject("routeLabelData").getString("waybillNo"));
        }
        return resp;
    }

    /**
     * 订单路由查询
     */
    public SFRouteResponse routeQuery(SFRouteQuery query) {
        String mgsData = JSON.toJSONString(query);
        String result = http("EXP_RECE_SEARCH_ROUTES", mgsData);
        log.info("routeQuery result:{}", result);
        JSONObject jsonObject = JSONObject.parseObject(result);
        String code = jsonObject.getString("apiResultCode");
        if (!"A1000".equals(code)) {
            log.error("顺丰订单路由查询结果,查询异常,result: {}", result);
            throw new LogisticsException("顺丰路由查询异常");
        }
        JSONObject apiResultData = jsonObject.getJSONObject("apiResultData");
        Boolean success = apiResultData.getBoolean("success");
        if (!success) {
            log.error("顺丰订单路由查询失败，result:{}", result);
            throw new LogisticsException(apiResultData.getString("errorMsg"));
        } else {
            return JSON.parseObject(apiResultData.getString("msgData"), SFRouteResponse.class);
        }
    }

    private String http(String serviceCode, String mgsData) {
        String timestamp = System.currentTimeMillis() + "";
        // 发送快递参数处理
        MultiValueMap<String, Object> sendBody = new LinkedMultiValueMap<>();
        // 合作伙伴编码（由顺丰分配）
        sendBody.add("partnerID", TEST_PARTNER_ID);
        // 请求唯一号UUID
        sendBody.add("requestID", IdUtils.fastSimpleUUID());
        // 接口服务代码取消订单
        sendBody.add("serviceCode", serviceCode);
        // 调用接口时间戳
        sendBody.add("timestamp", timestamp);
        // 数字签名
        sendBody.add("msgDigest", genDigest(timestamp, mgsData, TEST_MD5_KEY));
        // 业务数据报文
        sendBody.add("msgData", mgsData);
        //设置请求头参数
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-type", "application/x-www-form-urlencoded;charset=UTF-8");
        //发送请求
        HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<>(sendBody, headers);
        ResponseEntity<String> result = restTemplate.postForEntity(TEST_URL, formEntity, String.class);
        return result.getBody();
    }


    /**
     * 业务数据加密
     */
    private String genDigest(String timestamp, String mgsData, String md5key) {
        //将业务报文+时间戳+秘钥组合成需加密的字符串(注意顺序)
        String toVerifyText = mgsData + timestamp + md5key;
        //因业务报文中可能包含加号、空格等特殊字符，需要urlEnCode处理
        try {
            toVerifyText = URLEncoder.encode(toVerifyText, "UTF-8");
            //进行Md5加密
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(toVerifyText.getBytes(StandardCharsets.UTF_8));
            byte[] md = md5.digest();
            //通过BASE64生成数字签名
            return new BASE64Encoder().encode(md);
        } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

}
